#pragma once 

#include"Sock.hpp"
#include<sys/select.h>
#define NUM (sizeof(fd_set)*8)
#define DFL_FD -1


//#define DFL_PORT 8080
#define NUM (sizeof(fd_set)*8)
#define DFL_FD -1

class SelectServer {
  private:
        int lsock; 
        int port;
        int fd_array[NUM];
  public:
      SelectServer(int _p = 8080) :port(_p)
      {}
     void InitServer(){
       //初始化数组来保存已经打开的文件描述符
        for (int i = 0; i < NUM; i++) {
            fd_array[i] = DFL_FD;                            
            }
        lsock = Sock::Socket();
        Sock::Setsockopt(lsock);
        Sock::Bind(lsock, port);
        Sock::Listen(lsock);
       
        fd_array[0] = lsock;//将第一个元素设置成监听套接字
}
     void AddFd2Array(int sock){
          int i = 0;
          for (; i < NUM; i++){
          if (fd_array[i] == DFL_FD) {  
             break;
             }
          }
          if (i >= NUM) {
            cerr << "fd array is full, close sock" << endl;
            close(sock);
          }
          else{
            fd_array[i] = sock;
            cout << "fd: " << sock << " add to select ..." << endl;
                                                                        
          }
                                          
}
     void DefFdFromArray(int index){
          if (index >= 0 && index < NUM){
              fd_array[index] = DFL_FD;
                                                        
              }
}
    void HandlerEvents(fd_set* rfds){
      //先判断文件描述符是否合法
         for (int i = 0; i < NUM; i++){
              if(fd_array[i] == DFL_FD){
                 continue;                                                     
              }//分为监听套接字和普通的套接字
              if(FD_ISSET(fd_array[i], rfds)) {
                   if (fd_array[i] == lsock) {
                                                                
                  int sock = Sock::Accept(lsock);
                      if (sock >= 0){                                                                   
                        cout << "get a new link ..." << endl;
                        AddFd2Array(sock);//监听套接字不能直接就读数据，要先添加到数组中
                                          //有可能连接建立好后不发数据了
                        }
                                                                                
                    }
              else{                
                    char buf[10240];
                    ssize_t s = recv(fd_array[i], buf, sizeof(buf), 0);
                    if (s > 0) {
                        buf[s] = 0;
                        cout << "client# " << buf << endl;
                                                                                                                                                       }
             else if (s == 0) {
                     cout << "clien quit" << endl;
                     //客户端退出关闭文件描述符和将位图中的清0
                     close(fd_array[i]);  
                     DefFdFromArray(i);}
             else {}
             }
           }
                                                
         }
}
      void Start(){
           int maxfd = DFL_FD;
           for (;;) {
                fd_set rfds;
                FD_ZERO(&rfds);//在栈上先将位图清0
                cout << "fd_array: ";
                for (int i = 0; i < NUM; i++) {
                    if (fd_array[i] != DFL_FD) {
                    cout << fd_array[i] << " ";
                    FD_SET(fd_array[i], &rfds);
                    if (maxfd < fd_array[i]){
                      maxfd = fd_array[i];//更新最大的maxfd
                    }
                  }
               }
                cout << endl;
                cout << "begin select ..." << endl;
                switch (select(maxfd + 1, &rfds, nullptr, nullptr, nullptr)){
                  //select的3种返回值
                  case 0:
                     cout << "timeout ..." << endl; 
                     break;
                  case -1:        
                     cerr << "select error!" << endl; 
                     break; 
                  default:
                     //事件就绪
                     HandlerEvents(&rfds);
                     break;
                }
          }
}
     ~SelectServer()
    {}

};
